Guía de implementación de pruebas funcionales en la Plataforma CI/CD Corporativa

Información general

Icono guías
Tipo de recurso
Guía
Etiquetas

Descripción

Las pruebas funcionales son aquellas que verifican que un software o una aplicación funciona correctamente respecto a los requisitos del proyecto. Se centran en probar las funcionalidades del sistema, por ejemplo, que un botón haga lo que se espera de él o que un formulario se envía correctamente.

Las pruebas funcionales se usan para:

  • Asegurar que el sistema soporta todos los flujos funcionales para los que está diseñado y esta alineado con los requisitos o especificaciones indicados por la capa de negocio.
  • Detectar fallos o comportamientos no esperados que afectan negativamente a la experiencia de usuario.
  • Mejorar la calidad del software, minimizando las incidencias  
  • Ayudar a detectar problemas en las primeras fases del desarrollo, lo que reduce costes a largo plazo.

Las pruebas funcionales suelen registrarse en un documento llamado Plan de pruebas. En él se registra el conjunto de escenarios de prueba, que cubren el testeo de las funcionalidades de la aplicación o sistema. Un escenario de prueba es una descripción de alto nivel de una situación o un evento que agrupa distintos casos de prueba.  Ayuda a los participantes del proceso a comprender mejor el contexto y el comportamiento de la aplicación. 

Una vez construido el plan de pruebas, es conveniente identificar cuáles de los escenarios forman parte de la regresión (pruebas que deben ser ejecutadas con cualquier cambio en la aplicación, para cerciorarse que las funcionalidades principales de la aplicación continúen funcionando correctamente). Este conjunto de pruebas de regresión son candidatas para automatizar, dado que es necesario ejecutarlas con cada subida a producción, ahorrando tiempo a los equipos de desarrollo y asegurando que una parte de la aplicación no falla por el cambio en otra funcionalidad. 

Automatización de pruebas funcionales 

La automatización de pruebas consiste en el uso de herramientas y scripts para ejecutar pruebas funcionales de manera desatendida, simulando a un usuario final. Esto hace que las pruebas se ejecuten más rápido, de manera más fiable y sin necesidad de ejecutarlas manualmente. El objetivo final de la automatización es que sea lanzada de manera automática por los mecanismos de los pipelines tras cada despliegue. De esta manera se proporciona al equipo de desarrollo los resultados de la ejecución, detectando así errores del software. En el caso de detectar errores, si procede, se parará la promoción del software a un entorno dependiendo de los fallos.

Dado que el código generado requiere de cierto nivel técnico, para que estas pruebas automáticas sean comprensibles a determinados perfiles más funcionales, se creó el Behaviour-Driven Development (o BDD).  Su finalidad es que desarrolladores, gestores de proyectos y equipos de negocio colaboren y utilicen un lenguaje común. Se centra es describir con un lenguaje natural, el comportamiento de la aplicación desde el punto de vista de un usuario final.

BDD nos permite escribir los distintos flujos funcionales como características o features de la aplicación usando un lenguaje simple llamado Gherkin, eliminando la necesidad de entender código técnico para otros perfiles no técnicos. Este lenguaje es usado como base por diversas herramientas de automatización, por ejemplo, Cucumber.

Estas features recogen las características y funcionalidades del sistema a probar. Por ejemplo, una feature iniciar sesión podría ser “Dada la página de inicio de la aplicación, cuando el usuario introduzca su usuario y password, entonces le aparece la página con el menú principal”. En algunos casos, los pasos seguidos en las features requieren que utilicemos un valor para probarlos. En el ejemplo anterior, la función de “iniciar sesión” necesitaría usar el valor de usuario y contraseña utilizado para registrarse. Por último, todas las pruebas tienen un resultado esperado, que no es otra cosa que el comportamiento que se espera del sistema después de realizar las acciones descritas en las pruebas.

En definitiva, con BDD podemos escribir pruebas sobre nuestra aplicación en un lenguaje entendible para diversos perfiles y facilitar la colaboración entre ellos. 

BDD, Cucumber y Gherkin 

BDD (Behaviour-Driven Development) es una técnica de desarrollo que fomenta la colaboración entre diversos perfiles dentro del desarrollo de una aplicación (desarrolladores, testers, etc). Para ello, esta técnica se basa en escribir las especificaciones del software de una forma simple y comprensible para todos estos perfiles. Esta forma simple y comprensible se conoce como Lenguaje Gherkin. 

La estructura del lenguaje Gherkin es la siguiente:

  • Feature: Descripción de la funcionalidad a desarrollar. Una feature contiene uno o varios escenarios.
  • Scenario: Características que se tienen que desarrollar para conseguir la feature.  Se describen con las siguientes sentencias:
  • Given: Precondiciones que han de darse para poder ejecutar el escenario. Pueden     concatenarse con un and.
  • When: Acciones que se van a realizar en el escenario. También pueden concatenarse con un And.
  • Then: Resultado esperado de las acciones ejecutadas. 

Un ejemplo pudiera ser: 

Feature “Logarse en la aplicación”
Scenario: Iniciar sesión a través de la página web
	Given Un usuario que abre la página de inicio de la aplicación en el navegador
	And Ya tiene un usuario porque se ha registrado
	When El usuario introduce su nombre de usuario en el campo User
	And Introduce su password en el campo password

El lenguaje Gherkin es usado por múltiples herramientas que lo parsean para facilitar su automatización. Cucumber es la herramienta que se utilizará para implementar la metodología BDD, permitiendo ejecutar descripciones funcionales en texto plano como pruebas de software automatizadas.

Cucumber se basa en recoger estas especificaciones funcionales de la aplicación escritas en Gherkin en unos archivos llamados Features. Cada feature contiene funcionalidades del sistema y describe una parte del comportamiento del software, por ejemplo: el sistema permitirá a los usuarios iniciar sesión desde el endpoint del login. Un ejemplo de feature es: 

Estas features, en algunas ocasiones, pueden tener parámetros de entrada. Estos valores se usan para una función específica, como por ejemplo el usuario y la contraseña en la función de login. Por ejemplo, arriba se puede ver que la url, el navegador, el nombre del proyecto y el texto de búsqueda son parámetros porque van entre comillas

Por último, cada feature tiene un resultado esperado que está recogido en los requisitos funcionales del software. Este resultado nos indicará qué tiene que ocurrir después de seguir una serie de pasos, y debe ser comprobado dentro de las features.  El resultado esperado de ejecutar la feature anterior sería el siguiente: 

Aquí se puede ver los pasos seguidos por la feature y que todos tienen resultado PASS.

Así, todas las features cubrirán los flujos funcionales de los usuarios finales y comprobará que todo funciona como está especificado.
Como podemos ver, con BDD y Cucumber definimos las pruebas como si se tratase de escribir en lenguaje natural. Para una mayor información de Cucumber estas serían buenas prácticas y esta es su página oficial

  1. Las Feature deben probar partes o funcionalidades de una App y no la App al completo: Es una parte del software final que funciona y que además será de gran valor para los usuarios.
  2. Usar el mismo idioma que los clientes: Si quieres usar un idioma específico simplemente, en la primera línea de la Feature, tendrás que poner #language: <código idioma>, por ejemplo en español #language: es.
  3. Organizar las Feature: Una forma útil de organizar los Scenarios por la rapidez con que se ejecuten. Pero puedes usar la organización que más se adecue a vosotros.
  4. Usar etiquetas: Las etiquetas son una muy buena forma de agrupar y organizar los Scenarios y las Feature. Para añadir una etiqueta basta con poner el símbolo @ y el texto que se desee.
  5. Escribir Scenarios lo más Independientes posible: Lo ideal es que no haya ningún tipo de acoplamiento e independencia entre los Scenarios, que sean lo más independientes y autónomos unos de otros.  

Framework de automatización

El framework escogido para realizar las pruebas funcionales automatizadas es Selenide.

Selenide es una herramienta basada en Selenium que incorpora muchas mejoras haciéndola una herramienta de testing. Selenide incorpora aserciones, que son básicas para poder hacer testing. Además, Selenide permite hacer las mismas cosas que Selenium pero con mucho menos código y de manera más intuitiva. Selenide se encarga de gestionar automáticamente varias tareas que de otro modo es necesario escribir manualmente en Selenium, como manejar errores comunes y esperar a que los elementos aparezcan. 

Ventajas de Selenide 

Menos código para hacer lo mismo

En Selenium si queremos hacer un click a un botón es necesario esperar a que el botón esté disponible para hacerlo, lo que añade complejidad y cantidad de código.

Ejemplo de código de Selenium para hacer click: 

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
wait.until(ExpectedConditions.elementToBeClickable(element));
element.click();

El mismo código en Selenide:

$("#submit-button").click(); 

Selenide además de reducir el código a dos líneas, se encarga de manejar automáticamente las esperas y problemas comunes que puedan surgir, como elementos que no están visibles o disponibles inmediatamente. 

Automatización de esperas 

Una de las tareas más tediosas cuando se trabaja con Selenium es gestionar las "esperas". Esto se refiere a esperar a que un elemento esté listo para ser interactuando (como un botón que aparece en pantalla). En Selenium, se debe configurar las esperas manualmente cada vez que se interactua con un elemento.

En Selenide, la gestión de esperas viene integrada en la propia herramienta, la cual se asegura de que el elemento esté disponible antes de interactuar con él, sin que escribas una sola línea extra para ello. Esto no solo ahorra tiempo, sino que también reduce el riesgo de cometer errores. 

Manejos de errores automáticos 

Otro desafío común con Selenium es lidiar con los errores. Si un elemento no se encuentra, o si el navegador tiene algún problema, hay que escribir mucho código para manejar esas situaciones. En Selenide, este tipo de problemas son gestionados automáticamente, lo que hace las pruebas más robustas y menos propensas a fallar. 

Navegadores y Lenguajes soportados por Selenide 

Selenide es una herramienta de automatización de pruebas funcionales para aplicaciones web. Facilita la escritura de pruebas de forma más rápida y sencilla que herramientas similares como Selenium. 

Selenide está diseñado principalmente para desarrollar con Java, aunque también soporta Kotlin. En esta primera etapa, únicamente se soportará java como lenguaje de programación.  

Selenide funciona con los principales navegadores web: Google Chrome, Mozilla Firefox, Microsoft Edge y en sistemas Mac funciona con Safari. 

Selenium GRID  

Selenium Grid es una herramienta que permite ejecutar pruebas automáticas de aplicaciones web de manera simultánea en diferentes dispositivos, navegadores y sistemas operativos. Esto significa que, en lugar de probar una aplicación en un solo entorno, puedes hacerlo en varios al mismo tiempo, lo que ahorra tiempo y esfuerzo. Es especialmente útil cuando se necesita validar el funcionamiento de una página en múltiples configuraciones de manera rápida y eficiente.

Hay un Selenium GRID desplegado, que se puede consultar el enlace en las herramientas de la junta. Para acceder a él mediante el código se haría mediante un paso en nuestras pruebas como el siguiente: 

Configuration.browser = "chrome";
Configuration.headless = false;
Configuration.timeout = TimeUnit.SECONDS.toMillis(10);
Configuration.baseUrl = ConfigHelper.getResource(ConfigHelper.getResource("env"));
Configuration.remote = ConfigHelper.getResource("grid-" + ConfigHelper.getResource("env"));
Configuration.browserCapabilities = dc.driverOptions("chrome");
Selenide.open("/"); 
test=https://vea-frontend-svc-dev.apps.paas-des-cica.junta-andalucia.es/inicio 
pre=https://vea-frontend-svc-pre.apps.paas-des-cica.junta-andalucia.es/inicio 
grid= [URL Selenium-grid] 

Como podemos ver en el código anterior:

  • Configuration.remote = Esta primera línea de código es donde indicamos el nombre de la variable dentro del Properties que almacena el valor asociado al Selenium Grid
  • grid-test = Valor de la URL asociada al Selenium Grid y que varía según el entorno.

En definitiva, para usar Selenide en un Selenium Grid se hace desde el código de pruebas. Para ello, es necesario indicar a la clase Configuration cuál es la URL del Selenium Grid. 

Inicialización y Organización del proyecto 

Independientemente de la aplicación sobre la que se van a hacer las pruebas, se proporcionará un arquetipo para hacer las pruebas funcionales. Si la aplicación a probar no está hecha en lenguaje Java, se creará un repositorio a parte cuyo nombre será <aplicativo>_e2e para usar este arquetipo. Si la aplicación es Java, se incorporará al proyecto de la aplicación. 

Stack tecnológico Java 

Cuando tenemos un proyecto hecho en el stack tecnológico Java el procedimiento va a ser incorporar las pruebas funcionales de Selenide al proyecto. Hay que tener en cuenta el proyecto actual a 3 niveles:

  • Estructura del proyecto
  • Archivos nuevos de las pruebas funcionales (configuración y de las propias pruebas)
  • Plugins y perfiles en el pom.xml. 
Estructura del proyecto 

Es necesario que las pruebas funcionales tengan las “test sources” y “test resources” de Maven en la misma carpeta que el resto de los “test” (Unitarios, API...), que son las típicas “src/test/java” y “src/test/resources”.  

Dentro de ellas, se aloja la estructura de carpetas que da nombre al proyecto y donde se encontrarán las carpetas Selenide y Features donde se encuentra todos los archivos base para la automatización. Debe quedar todo como en la captura del proyecto de ejemplo jpetstore6:

Como se puede ver en la captura, la carpeta TEST contiene la carpeta JAVA (como test source) y la carpeta RESOURCES (como test resources). Por último, se añade las carpetas org/mybatis/jpetstore dentro, ya que es una convención muy extendida en proyectos Maven el añadir carpetas con el groupId y el artifactId del pom del proyecto.  

Es decir, es necesario añadir la carpeta TEST (junto con JAVA y RESOURCES) para asilar las pruebas de integración (que en este caso también son funcionales y end to end) y las carpetas que coinciden con el groupID y artifactID del proyecto (en el ejemplo son org.mybatis.jpetstore, pero puede variar por proyecto).

Definición de archivos de importancia 

Los archivos y estructura de carpetas que se añaden son los siguientes:

Explicación de los mismos:

  • Carpeta Java: en esta carpeta se introducen todos los archivos que contienen clases de test, tanto pasos como ficheros de configuración o de utilidades.  
    • Primero se crea una carpeta selenide.
    • Dentro de “selenide” se ubica la carpeta “steps” con todos los pasos utilizados por las pruebas de cucumber y que son llamados desde los archivos feature.  

      Se recomienda encarecidamente organizar los pasos en archivos y carpetas ya que cada paso de prueba se puede escribir una vez en código y ser reutilizado en cualquier número de pruebas. Ejemplo: se puede y recomienda organizar el login de nuestra aplicación dentro de una carpeta /login y un archivo llamado <nuestra_aplicacion>LoginSteps.java. 

      En el ejemplo, está el fichero JPetstoreSteps que contiene todos los pasos: desde el login hasta la navegación por la página.

    • Los archivos añadidos son los siguientes:
      • ConfigHelper: lector de archivos properties para buscar variables en tiempo de ejecución. Está configurado para abrir src/it/resources/test-data.properties, y en base al entorno utilizado utiliza la url y el grid correspondiente: 

        test=https://jpetstore-test.apps.paas-des-cica.junta-andalucia.es/jpetstore 
        pre=https://jpetstore-pre.apps.paas-des-cica.junta-andalucia.es/jpetstore 
                                                                                                        grid =[URL Selenium-grid] 
        Configuration.remote = ConfigHelper.get("grid-"); 
        Configuration.baseUrl = ConfigHelper.get(ConfigHelper.get("env")); 

        Se usan en esta parte de código:

        Si el valor de ”env” es ’pre’, usará las variables” pre” con sus respectivos valores. Estos valores son orientados a pruebas, y este fichero debe ser modificado dependiendo del proyecto. Sobretodo, teniendo en cuenta que el objetivo final es ejecutar en productivo, por lo que es necesario añadir también las variables homónimas de dicho entorno productivo.

        • CucumberHooks: Esta clase permite definir unos métodos de java que se ejecutan antes o después de cada prueba. Sirve para definir precondiciones, o teardowns. 
          Añadir una fila al log de cucumber de que empieza y acaba la prueba y forzar que se cierre el webdriver entre pruebas. Esto es para asegurar que cada prueba funcional genera un video en el selenium grid. 
        • CucumberIT: Esta clase java es el runner de Cucumber. Un runner es una clase que al ejecutarse orquesta y posibilita la ejecución, en este caso, de las pruebas de Cucumber. Contiene rutas a la ubicación de los recursos de pruebas (features), la propiedad “glue” que indica la carpeta donde están todos los ficheros de pruebas y de configuración de cucumber y configuración de los reportes que queremos generar: serenity, pretty y timeline. 
        • DriverConfig: Esta clase java aísla la configuración que se le adjunta a los webdrivers de selenide. Por defecto ignora los certificados SSL inseguros, como recomiendan las convenciones de Selenium.
        • SessionLogger: esta clase java da soporte a la fase de subir videos a Consigna dentro del PostSync de Jenkins. Esta clase crea en la carpeta /target un fichero que se llama “sessionId-<fecha-de-hoy>.txt donde cada línea contiene el sessionId de cada escenario. Los diferentes selenium grid están configurados para guardar en un pvc los videos de ejecución, cuyo nombre es la sessionId. De esta manera, en cada ejecución, se puede identificar cuáles son los videos de las pruebas ejecutadas. Es necesaria su adición al proyecto si queremos evitar errores en el ciclo de vida de Cadena Única, aunque esta clase no esté directamente relacionada con las pruebas. También es necesario este archivo debido a que en Selenium no es posible cambiar la sessionID por una customizable.
  • Carpeta Resources: en esta carpeta se guardan los ficheros de recursos usados en las pruebas de integración, los ficheros de configuración y los ficheros de pruebas llamados “features”. 
    • Features: son las pruebas funcionales. Son una colección de ficheros reconocibles para Cucumber en formato ‘feature’. Contienen los pasos definidos en lenguaje natural (Gherkin). 

      Se recomienda organizar las pruebas por ficheros y carpetas y no poner todas las pruebas en un mismo archivo. 

    • junit-platform.properties : es un fichero de configuración del comportamiento de Cucumber. Se llama así porque Cucumber se apoya en Junit5 para ejecutar. Contiene configuración para la ejecución de pruebas en paralelo, actualmente desactivado. Además, contiene también la propiedad “glue” que está definida en el runner “CucumberIT”. 

      	cucumber.execution.parallel.enabled=false 
      	cucumber.execution.parallel.config.strategy=fixed 
      	cucumber.execution.parallel.config.fixed.parallelism=2 
      	cucumber.execution.parallel.config.fixed.max-pool-size=10 
      	cucumber.junit-platform.naming-strategy=long 
      	cucumber.glue=org.mybatis.jpetstore.selenide 
    • Serenity.conf: es un fichero que configura los reportes de Serenity que se generan. Define el nombre, rutas donde generar el reporte y estrategias de toma de capturas de pantalla. 

      	serenity { 
       	project.name=JpetStore6 Serenity 
       	outputDirectory=target/site/serenity 
       	reportsFolder=target/site/serenity 
       	take.screenshots = FOR_EACH_ACTION 
       	screenshot.at.failure = true 
       	reports.show.screenshots = true 
       	report.include.failure.screenshots = true 
       	report.relative.urls = false 
      	} 

En la raíz del proyecto, al lado del pom.xml, es necesario añadir el siguiente archivo de configuración:

  • .gitignore: es necesario quitar la línea que ignora ficheros properties, por el siguiente archivo.
  • serenity.properties:  Es otro fichero de configuración de los reportes de Serenity. Se duplica para asegurar que detecta la configuración.

    	serenity.project.name=ADA Serenity 
    	serenity.outputDirectory=target/site/serenity 
    	selenide.reportsFolder=target/site/serenity 
    	serenity.take.screenshots=FOR_EACH_ACTION 
    	serenity.screenshot.at.failure=true 
    	serenity.reports.show.screenshots=true 
    	serenity.report.include.failure.screenshots=true 
    	serenity.report.relative.urls=false 
Plugins y perfiles 

Es necesario cambiar el fichero pom.xml para posibilitar toda la configuración anterior. También, es necesario modificar el fichero para separar en etapas de Maven la ejecución de los distintos tipos de prueba y, por último, para indicar a Maven que la carpeta IT contiene test-sources y test-resources que deben de ser compiladas también.

Se indican los cambios necesarios de arriba a abajo en el fichero pom: 

  • Properties con versiones de dependencias de IT: en la sección <properties>, al final, se añaden las siguientes líneas que indican versiones de dependencias. De esta manera, podemos cambiarlas fácilmente. También se añaden dos variables, skipITs y skipUTs que se usarán para aislar las pruebas unitarias en la etapa ‘test’ y las pruebas funcionales en la etapa ‘verify’.
  • Se añaden las dependencias: 
    • com.google.guava: se utiliza para simplificar el manejo de colecciones, objetos inmutables, y utilidades como cachés, Optional, y estructuras avanzadas, facilitando un código más limpio y eficiente en pruebas, servicios y lógica de negocio en Java.
    • cucumber-java, cucumber-junit, cucumber-junit-platform-engine: librerías básicas que permiten definir y ejecutar pruebas con Cucumber.
    • selenide: librería que permite definir pasos de prueba con Selenide.
    • junit-jupiter, junit-jupiter-api, junit-platform-suite: permite definir suites de Uunit y actúa de soporte a Cucumber.
    • serenity-core, serenity-cucumber, serenity-junit5: librerías que permiten generar reportes de Serenity exclusivamente de la librería Cucumber.
  • Plugins de Maven:
    • maven-failsafe-plugin: este plugin define la ejecución de pruebas de integración y se enlaza a la etapa verify de Maven.  
      Es necesario que se añada en la parte de pluginManagement lo siguiente: includes para seleccionar los ficheros java a ejecutar (en este caso los que acaban en IT.java, como CucumberIT.java), se desactiva la ejecución en paralelo y se configura para que en caso de fallo de una prueba funcional siga ejecutando las otras. Por último, le indicamos que use la variable skipITs, que detallaremos en la parte de perfiles de ejecución. 
	<artifactId>maven-failsafe-plugin</artifactId> 
          	<version>3.5.2</version> 
          	<configuration> 
            	<skipITs>${skipITs}</skipITs> 
            	<argLine>-Djavax.net.ssl.trustStore=/var/jenkins_home/certificados/mykeystore.jks 
                    	-Djavax.net.ssl.trustStorePassword=mypassword</argLine> 
            	<includes> 
              	<include>**/*IT.java</include> 
            	</includes> 
            	<parallel>none</parallel> 
            	<testFailureIgnore>true</testFailureIgnore> 
          	</configuration> 
        	</plugin> 

Por otro lado, en la parte de executions es necesario añadir que se quiere usar este plugin en la fase verify e integration-test.

	    <plugin> 
        	<groupId>org.apache.maven.plugins</groupId> 
        	<artifactId>maven-failsafe-plugin</artifactId> 
       		<version>3.5.2</version> 
       		<executions> 
       			<execution> 
           			<id>integration-tests</id> 
           			<goals> 
           				<goal>integration-test</goal> 
              			<goal>verify</goal> 
            		</goals> 
          		</execution> 
        	</executions> 
      	</plugin> 

 

  • maven-surefire-plugin: este plugin está asociado a la etapa test de Maven y se encarga de ejecutar las pruebas unitarias. En la parte de pluginManagement es necesario indicar que ejecute archivos que acaban en Test.java, y asegurar que los test unitarios de la ruta src/test/java cumplen esta nomenclatura.
		<plugin> 
         	<groupId>org.apache.maven.plugins</groupId> 
         	<artifactId>maven-surefire-plugin</artifactId> 
       		<version>3.2.5</version> 
       		<configuration> 
      			<useModulePath>false</useModulePath> 
       			<includes> 
            		<include>**/*Test.java</include> 
           		</includes> 
         	</configuration> 
		</plugin> 
  • build-helper-maven-plugin: Este plugin se utiliza para indicar a maven que hay test-sources y test-resources en la carpeta src/it. Se utiliza solo la parte executions, en tiempo de ejecución se le pasa las indicaciones a Maven.
  • maven-compiler-plugin: Este plugin se utiliza para indicar que se compile la carpeta src/it entera:
  • maven-resources-plugin: Con este plugin le indicamos a Maven que busque archivos de test-resources en src/it/resources además de src/test/resources.
  • serenity-maven-plugin: se encarga de definir una etapa del propio plugin y de configurarla para ejecutar como etapa de Maven al final. 
Ejecutar pruebas funcionales
  • Debido a estos perfiles nuevos, para ejecutar las pruebas funcionales es necesario usar el comando mvn clean verify -Pfunctional-tests

Resto de Stacks tecnológicos 

El arquetipo de proyecto aislado de pruebas funcionales ya tiene todo preparado y podemos usarlo de base para hacer las pruebas.  

En la siguiente captura se puede ver el arquetipo base: 

Los archivos que contiene son los siguientes:

  • pom.xml: este fichero recoge información del proyecto, dependencias necesarias en el mismo y por último la configuración a la hora de ejecutarlo. En el proyecto, ya está configurado con todas las dependencias necesarias para utilizar Cucumber y Selenide. En caso de que cambiasen las versiones de las mismas debería editarse para coger las versiones correctas. Es recomendable tener las librerías actualizadas a la última versión y se pueden encontrar todas las dependencias necesarias en el pom del arquetipo indicado anteriormente.
  • .../selenide/cucumber: esta es la carpeta que contiene el código de Selenide y el de Cucumber.
  • CucumberHooks: este fichero contiene lo que se denominan Hooks en cualquier librería de testing. Los Hooks son métodos que se usan para controlar el flujo de los tests, ejecutándose antes y después de cada paso, test o grupo de ellos. No es necesario hacer nada con este archivo, solo sirve para implementar nuevas funcionalidades de testing, como, por ejemplo: librerías de reportes o manejo de datos para los tests (cargar datos antes de cada escenario, actualizar datos después, etc).
  • RunCucumberIT: es el fichero que se encarga de ejecutar las features. El fichero se puede utilizar tal cual, ejecutando todas las features. Si queremos filtrar las features, podemos usar “@CucumberOptions(tags = "@smoke and @sanity")” filtrando las features utilizando tags. Es decir, si se indica un filtrado por las tag @smoke y @sanity se van a ejecutar las Features y los Scenarios que tengan esas tags indicadas solamente. Estos tags se definen en los archivos feature.  Se pueden añadir a la palabra clave “Feature” o a la palabra clave “Scenario”. Con colocarlas encima es suficiente. Se utilizan para definir a qué tipo de tests pertenecen (smoke, sanity) o a qué modulo pertenecen principalmente, aunque siempre se pueden crear tags customizadas a medida del proyecto.
  • Carpeta steps: esta carpeta contiene los pasos de Selenide usados en las features. Son una colección de ficheros java distribuidos en carpetas según las diferentes partes de la aplicación.
  • Carpeta features: es la carpeta que contiene las features de cucumber que utilizan los pasos para formar flujos funcionales. Las features siguen la sintaxis Gherkin para definir los Pasos de los Scenarios. Estos pasos se enlazan a los pasos dentro de los ficheros Steps de Selenide mediante un tag y el mismo texto del paso de la feature. De esta manera podemos usar un paso en las features que queramos y llenarlo de código que aporta funcionalidad. Como hemos comentado en la parte de BDD, una feature es un archivo de Cucumber que implementa BDD usando lenguaje Gherkin. Contiene pasos en lenguaje natural que comienzan con las palabras clave Given, When, Then y And. Veremos un ejemplo en el siguiente apartado. 

Guía de creación de una prueba funcional nueva 

Cuando ya existe la estructura de archivos explicada en el punto anterior, podemos empezar a crear nuestras pruebas funcionales. Vamos a crear una prueba de login en TEO usando el SSO Login:  

Creación de una Feature 

Para dar una mayor claridad y como buena práctica, se recomienda organizar las features en carpetas.  

En el archivo, usando el lenguaje Gherkin, describimos la funcionalidad a probar, que sigue la sintaxis de Cucumber:  


Primero va el título del Scenario, seguido de los pasos con los prefijos Gherkin Given, When y Then. Los pasos han de estar escritos siguiendo el principio BDD, es decir, han de definir de manera concisa y concreta qué hace ese paso.  

Segun la configuración implementada para el arquetipo proporcionado, los archivos .feature deben siempre almacenarse en la ruta “src/test/resources/” y dentro de la carpeta definida en el runner de Cucumber (../Selenide/Cucumber)

Creación de los Pasos de Selenide

Para que la feature se pueda ejecutar, es necesario añadir el código de Selenide en un fichero java dentro de la carpeta Steps.  Se recomienda crear un fichero por cada módulo de la aplicación, metiendo dentro del fichero todos los pasos que afecten a ese módulo. Para nuestro ejemplo, en el arquetipo se proporcionan un fichero java:

  • JPetstoreSteps.java: Corresponde al componente encargado de contener toda la lógica asociada a los pasos del Test a realizar.

Según la configuración implementada para el arquetipo proporcionado, los archivos asociados a los Steps definidos deben siempre almacenarse en la ruta “src/test/java/” y dentro de la carpeta definida en el runner de Cucumber (../Selenide/Cucumber).

Paso Inicial ‘Given abro la página principal en el navegador "<browser>"’

Este primer paso contiene ademas del acceso a la URL principal del Test, tambien la configuracion inicial a realizar en él:

	@Given("abro la pagina principal en el navegador {string}")
	public void abroLaPaginaPrincipal(String bs) {
	Configuration.browser = bs;
	Configuration.browserCapabilities = dc.driverOptions(bs);
	Configuration.headless = false;
	Configuration.timeout = TimeUnit.SECONDS.toMillis(10);
	Configuration.baseUrl = ConfigHelper.getResource(ConfigHelper.getResource("env"));
	Selenide.open("/");
	SessionLogger.logSessionId();
	assertThat(title()).isEqualTo(ConfigHelper.getResource("web_name"));
	}

Cuando se cree un nuevo Tests este paso debe mantenerse de cara a mantener la configuración básica de los Tests de Selenide. Únicamente debe de cambiarse la informacion asociada a la configuracion del Test contenida en el archivo .properties (src/test/resources)

Resto de Pasos

A partir de este punto se deben de definir la lógica asociada a cada uno de los pasos creados en el archivo .feature. En este caso, los pasos de ejemplo contenidos en el arquetipo proporcionado muestran las acciones necesarias para llevar a cabo la acción o acciones definidas en el paso en cuestión:

Para ello, Selenide nos proporciona distintas operaciones como son el ‘Click’, Escritura de Texto, Comprobación de Atributos o Condiciones...

Estas operativas pueden realizar utilizando directamente la Clase “Selenide”:

O mediante el uso de un elemento “SelenideElement”:

Gestión de Elementos Web mediante sistema PageFactory

El sistema de gestión de elementos Web llamado PageFactory aporta las siguientes ventajas al Framework de automatización:

  • Reducción de Duplicación de Código: Page Factory ayuda a evitar la repetición al agrupar la declaración y localización de elementos web, permitiendo su reutilización en múltiples tests.  
  • Mejor Mantenimiento y Escalabilidad: Cuando la interfaz de usuario cambia, solo necesitas actualizar la clase del Page Object correspondiente, en lugar de buscar y modificar cada caso de prueba. Esto hace el marco de pruebas más fácil de mantener y escalar.  
  • Código Más Legible y Organizado: Al separar la lógica de los detalles de la página y los elementos web, el código se vuelve más limpio y entendible para los desarrolladores y testers.  
  • Inicialización Eficiente de Elementos: Page Factory simplifica la inicialización de los elementos web usando anotaciones como @FindBy, que pueden definir localizadores por ID, CSS, XPath, etc., con una sola línea de código. 
  •  Facilita la Implementación del Page Object Model (POM): Page Factory es una forma de implementar el patrón POM, que es fundamental para crear marcos de automatización de pruebas robustos, eficientes y fáciles de mantener.  

Reutilización de Elementos: Los elementos web declarados con @FindBy en la Page Factory pueden ser utilizados a través de diferentes métodos dentro de la misma clase, y también por otros tests que compartan la misma lógica de la página.  

Así, para la creación de nuevos elementos tipo SelenideElement que se utilizarán para la definición de la lógica asociada a cada Step. De esta manera, se debe de crear una clase Java, cuyo nombre se recomienda que finalice con el texto “...PageFactory.java”

Pese a no ser imprescindible para el funcionamiento del proyecto, los archivos asociados a los PageFactory definidos deben almacenarse en la ruta “src/test/java/” y dentro de la carpeta  (../Selenide/PageFactory).

Ejecución del código

Para ejecutar las pruebas se debe ejecutar el siguiente comando en la consola:  

mvn clean verify -DskipUTs=true -DskipITs=false -DgridUrl=remote -Dcucumber.filter.tags="@jpetstore"

Este comando ejecuta hasta la fase “verify” de Maven. Esto es debido a que en la fase “test” esta reservada para que se ejecute los Test No Funcionales, y la fase de “verify” para los Tests Funcionales de Selenide.

Así, se define los parámetros DskipUTs y -DskipITs para indicar si se realizan los Test No Funcionales (skipUTs) y/o los Test Funcionales. 

Por otro lado, DgridUrl nos indica si el lanzamiento es local o remoto, pensado sobre  todo para poder hacer lanzamientos locales durante el desarrollo de los Test, y Dcucumber.filter.tags que nos permite definir etiquetas asociadas los Tests en el archivo .feature, permitiendo también realizar un filtrado previo al lanzamiento.

Sin embargo, la plataforma CI/CD es capaz de lanzar estos test a través de Jenkins, por lo que el usuario no necesita lanzar ningún comando en la promoción del software. Toda esta información esta almacenada en el archivo “ci.json” 

Resultados, Evidencias y Visualización.

Mientras ejecuta en local, se abrirá una ventana del navegador escogido para la prueba y veremos que se van produciendo las operaciones descritas en la feature. En este caso, veremos cómo se accede a la URL de TEO, se pulsa el login y se introducen los datos de un usuario para hacer login.  

Al ejecutar el código anterior obtenemos el siguiente resultado en la consola:

El resultado indica que todos los pasos han ejecutado correctamente.  

Además del reporte de consola, al ejecutar se genera automáticamente un reporte en formato HTML en la ruta target/cucumber-html-report.html, al lado de la carpeta src. Se ve de la siguiente manera:

Como se ve en la imagen, el reporte se divide en dos secciones:

  • Una sección superior con resumen del resultado de todas las features ejecutadas.
  • Una sección inferior donde hay un desplegable por cada archivo feature ejecutado, con detalle del resultado de cada Scenario ejecutado. En la imagen podemos ver:
    • El nombre de la feature, en este caso Creating a task type petition on TEO
    • En este caso, podemos ver el resultado del Examples de la feature. Un Examples en una feature es una anotación que permite repetir un Tests tantas veces como sea necesario, cambiando en cada una de ellas una variable en función del valor definido para ella en cada caso. En este ejemplo, se repetiría 2 veces el Tests, cambiando el parámetro “browser” por Chrome y Firefox en cada ejecución realizada.

En Jenkins, estará configurado el Job para publicar los resultados de consola y HTML producidos por la ejecución. Al mismo tiempo, configurando el Selenium Grid para guardar los vídeos de ejecución y que no solo sean accesibles durante la propia ejecución y añadiendo un paso para publicarlos en Jenkins tendríamos accesible una prueba de vídeo para revisar las ejecuciones. Aun así, es recomendable guardar vídeos solamente si el job ha dado error para evitar necesidades de almacenaje elevadas.

Arquitectura de automatización de pruebas funcionales

En el siguiente diagrama se indica cómo quedaría la arquitectura de automatización de pruebas funcionales en la Paltaforma CI/CD. El código que implementa las pruebas automáticas se almacena en GitLab junto con el código de la aplicación. Tras el despliegue de la aplicación, se lanzan las pruebas automatizadas, utilizando Selenide para ello. Selenide se encargaría de conectarse al Selenium Grid para ejecutar las pruebas funcionales, accediendo a la aplicación desplegada a través del navegador. Cuando acabe, Selenide comunica los resultados a la herramienta de CI que se encarga de publicarlos y guardar un histórico.  

Enlaces de interés

Documentación de Selenide

Documentación de Cucumber: sintaxis Gherkin

Documentación de Cucumber: qué es el BDD

Documentación de Cucumber: paso de parámetros en los steps

Lista de todas las palabras claves en las condiciones de Selenide y métodos para usarlas

Uso de tags para filtrar features y scenarios.